/*
 * @# PaginationTag.java 2011-7-2 上午11:20:09
 * 
 * Copyright (C) 2010 - 2011 广州羿安信息科技有限公司
 * Guangzhou ianswer information technology co. ltd.
 * 
 * All rights reserved!
 */
package com.ianswer.px.common.tags;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;

import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.ianswer.common.orm.PageContext;
import com.ianswer.common.widget.Component;
import com.ianswer.common.widget.form.Button;
import com.ianswer.common.widget.form.TextField;
import com.ianswer.common.widget.media.Href;
import com.ianswer.common.widget.panel.Div;
import com.ianswer.common.widget.panel.Span;

/**
 * 分页控件.
 * 
 * @author alvin hwang
 */
public class PaginationTag extends TagSupport {

	private static final long serialVersionUID = -804482549906024214L;

	private static final int FIRST_PAGE = 1;
	private static final int DEFAULT_RESULTS_PER_PAGE = 10;
	private static final String DEFAULT_PAGINATION_CLASS = "pagination";
	private static final String DEFAULT_PAGE_PARAM = "_p";
	private static final String DEFAULT_RP_PARAM = "rp";
	private static final String DEFAULT_SHOW_TOTAL_PARAM = "_st";

	private static Logger log = Logger.getLogger(PaginationTag.class);

	/**
	 * 获取数据的action.
	 */
	private String action;
	/**
	 * 每页显示的个数.
	 */
	private int resultsPerPage = DEFAULT_RESULTS_PER_PAGE;
	/**
	 * 分页控件的样式.
	 */
	private String paginationClass = DEFAULT_PAGINATION_CLASS;
	/**
	 * 请求页码的参数名.
	 */
	private String pageParam = DEFAULT_PAGE_PARAM;
	/**
	 * 是否传递请求每页显示的个数的参数名.
	 */
	private String showRpParam = "true";
	/**
	 * 请求每页显示的个数的参数名.
	 */
	private String rpParam = DEFAULT_RP_PARAM;
	/**
	 * 请求是否计算总数的参数名.
	 */
	private String showTotalParam = DEFAULT_SHOW_TOTAL_PARAM;
	/**
	 * 是否计算总数?
	 */
	private String showTotal = "true";

	@Override
	public int doStartTag() throws JspException {
		JspWriter out = null;
		try {
			out = pageContext.getOut();
			out.print(buildPaginationHtml());

			out.flush();
		} catch (Exception e) {
			log.warn("PaginatorTag报错", e);
		}
		return SKIP_BODY;
	}

	private String buildPaginationHtml() {
		int total = BooleanUtils.toBoolean(showTotal) ? getTotal() : Integer.MAX_VALUE;
		if (total == 0 || resultsPerPage == 0) {
			return StringUtils.EMPTY;
		}

		int currentPage = fetchCurrentPage();
		int pageTotal = (int) Math.ceil(total / (resultsPerPage * 1.0));

		Div pagination = new Div();
		pagination.className(paginationClass);

		addPrevPageToPagination(pagination, currentPage);
		addPageToPagination(pagination, currentPage, pageTotal);
		addNextPageToPagination(pagination, currentPage, pageTotal);
		if (BooleanUtils.toBoolean(showTotal)) {
			addSkipPageToPagination(pagination, currentPage, pageTotal);
		}

		return pagination.toHtml();
	}

	private void addPrevPageToPagination(Component pagination, int currentPage) {
		if (currentPage == FIRST_PAGE) {
			Span start = new Span();
			start.className("start-page").add(new Span().add("上一页"));
			pagination.add(start);
			return;
		}
		Href prev = new Href();
		prev.href(buildPageAction(currentPage - 1));
		prev.className("prev-page").add(new Span().add("上一页"));
		pagination.add(prev);
	}

	private String buildPageAction(int page) {
		return buildPageAction(page, false);
	}

	private String buildDynPageAction() {
		return buildPageAction(0, true);
	}

	private String buildPageAction(int page, boolean dynPage) {
		StringBuffer href = new StringBuffer();
		String cxt = pageContext.getServletContext().getContextPath();
		if (!"/".equals(cxt)) {
			href.append(pageContext.getServletContext().getContextPath());
		}
		if (!StringUtils.startsWith(action, "/")) {
			href.append("/");
		}
		href.append(action);
		if (StringUtils.contains(action, '?')) {
			href.append('&');
		} else {
			href.append('?');
		}
		href.append(pageParam).append('=').append(dynPage ? "$page" : page);
		if (BooleanUtils.toBoolean(showRpParam)) {
			href.append('&').append(rpParam).append('=').append(resultsPerPage);
		}
		href.append('&').append(showTotalParam).append('=').append(showTotal);
		return href.toString();
	}

	private void addNextPageToPagination(Component pagination, int currentPage, int pageTotal) {
		if (currentPage == pageTotal) {
			Span end = new Span();
			end.className("end-page").add(new Span().add("下一页"));
			pagination.add(end);
			return;
		}
		Href next = new Href();
		next.href(buildPageAction(currentPage + 1));
		next.className("next-page").add(new Span().add("下一页"));
		pagination.add(next);
	}

	private void addPageToPagination(Component pagination, int currentPage, int pageTotal) {
		int showPaginatorCount = 9; // 只显示页码个数
		int breadPageWhen = 7; // 分开成两段时触发页码
		int currentPagetailCount = 4; // 中间部分显示在当前页码后面的, 最多显示的个数

		pagination.add(buildPage(1, currentPage));
		if (pageTotal == 1) {
			return;
		}
		pagination.add(buildPage(2, currentPage));
		if (pageTotal == 2) {
			return;
		}
		// 页数少于showPaginatorCount
		if (pageTotal <= showPaginatorCount) {
			for (int page = 3; page <= pageTotal; page++) {
				pagination.add(buildPage(page, currentPage));
			}
			return;
		}
		// 达到breadPageWhen时的页码
		if (currentPage >= breadPageWhen) {
			pagination.add(buildBreakPage());
			int breakPageStart = currentPage - 2;
			int breakPageEnd = currentPage + currentPagetailCount;
			if (breakPageEnd > pageTotal) {
				breakPageStart = currentPage - (breadPageWhen - (pageTotal - currentPage)) + 1;
				breakPageEnd = pageTotal;
			}
			for (; breakPageStart <= breakPageEnd; breakPageStart++) {
				pagination.add(buildPage(breakPageStart, currentPage));
			}
			if (breakPageEnd < pageTotal) {
				pagination.add(buildBreakPage());
			}
			return;
		}
		// 还没有达到breadPageWhen时的页码
		for (int page = 3; page <= showPaginatorCount; page++) {
			pagination.add(buildPage(page, currentPage));
		}
		pagination.add(buildBreakPage());
	}

	private Component buildPage(int pageNumber, int currentPage) {
		if (pageNumber == currentPage) {
			return new Span().className("current-page").add(String.valueOf(pageNumber));
		}
		Href page = new Href();
		page.href(buildPageAction(pageNumber));
		page.add(String.valueOf(pageNumber));
		return page;
	}

	private Component buildBreakPage() {
		return new Span().className("break-page").add("...");
	}

	private void addSkipPageToPagination(Component pagination, int currentPage, int pageTotal) {
		Span skip = new Span();
		skip.className("skip-page");
		skip.add("共" + pageTotal + "页  到第");
		int jumpPage = currentPage + 1;
		if (jumpPage > pageTotal) {
			jumpPage = 1;
		}
		skip.add(new TextField().size(3).value(String.valueOf(jumpPage)).id("jump-to-page")).add("页 ");
		Button button = new Button().normalButton();
		button.add("确定");
		StringBuffer clickEvent = new StringBuffer(100);
		clickEvent.append("var s=document.getElementById(\"jump-to-page\").value;if(s>").append(pageTotal).append(")")
				.append("s=").append(pageTotal).append(";location.href=\"").append(buildDynPageAction())
				.append("\".replace(\"$page\",s);");
		button.click(clickEvent.toString());
		skip.add(button);
		pagination.add(skip);
	}

	private int fetchCurrentPage() {
		String page = pageContext.getRequest().getParameter(pageParam);
		if (StringUtils.isBlank(page)) {
			return FIRST_PAGE;
		}
		return Integer.valueOf(page);
	}

	private int getTotal() {
		int recordCount = PageContext.getTotalElements().intValue();
		PageContext.clean();
		return recordCount;
	}

	public void setAction(String action) {
		this.action = action;
	}

	public void setResultsPerPage(int resultsPerPage) {
		this.resultsPerPage = resultsPerPage;
	}

	public void setPaginationClass(String paginationClass) {
		this.paginationClass = paginationClass;
	}

	public void setPageParam(String pageParam) {
		this.pageParam = pageParam;
	}

	public void setRpParam(String rpParam) {
		this.rpParam = rpParam;
	}

	public void setShowTotalParam(String showTotalParam) {
		this.showTotalParam = showTotalParam;
	}

	public void setShowTotal(String showTotal) {
		this.showTotal = showTotal;
	}

	public void setShowRpParam(String showRpParam) {
		this.showRpParam = showRpParam;
	}

}
